1 /*
2 * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package javax.swing.table;
27
28 import java.io.Serializable;
29 import java.util.Vector;
30 import java.util.Enumeration;
31 import javax.swing.event.TableModelEvent;
32
33
34 /**
35 * This is an implementation of <code>TableModel</code> that
36 * uses a <code>Vector</code> of <code>Vectors</code> to store the
37 * cell value objects.
38 * <p>
39 * <strong>Warning:</strong> <code>DefaultTableModel</code> returns a
40 * column class of <code>Object</code>. When
41 * <code>DefaultTableModel</code> is used with a
42 * <code>TableRowSorter</code> this will result in extensive use of
43 * <code>toString</code>, which for non-<code>String</code> data types
44 * is expensive. If you use <code>DefaultTableModel</code> with a
45 * <code>TableRowSorter</code> you are strongly encouraged to override
46 * <code>getColumnClass</code> to return the appropriate type.
47 * <p>
48 * <strong>Warning:</strong>
49 * Serialized objects of this class will not be compatible with
50 * future Swing releases. The current serialization support is
51 * appropriate for short term storage or RMI between applications running
52 * the same version of Swing. As of 1.4, support for long term storage
53 * of all JavaBeans<sup><font size="-2">TM</font></sup>
54 * has been added to the <code>java.beans</code> package.
55 * Please see {@link java.beans.XMLEncoder}.
56 *
57 * @author Philip Milne
58 *
59 * @see TableModel
60 * @see #getDataVector
61 */
62 public class DefaultTableModel extends AbstractTableModel implements Serializable {
63
64 //
65 // Instance Variables
66 //
67
68 /**
69 * The <code>Vector</code> of <code>Vectors</code> of
70 * <code>Object</code> values.
71 */
72 protected Vector dataVector;
73
74 /** The <code>Vector</code> of column identifiers. */
75 protected Vector columnIdentifiers;
76
77 //
78 // Constructors
79 //
80
81 /**
82 * Constructs a default <code>DefaultTableModel</code>
83 * which is a table of zero columns and zero rows.
84 */
85 public DefaultTableModel() {
86 this(0, 0);
87 }
88
89 private static Vector newVector(int size) {
90 Vector v = new Vector(size);
91 v.setSize(size);
92 return v;
93 }
94
95 /**
96 * Constructs a <code>DefaultTableModel</code> with
97 * <code>rowCount</code> and <code>columnCount</code> of
98 * <code>null</code> object values.
99 *
100 * @param rowCount the number of rows the table holds
101 * @param columnCount the number of columns the table holds
102 *
103 * @see #setValueAt
104 */
105 public DefaultTableModel(int rowCount, int columnCount) {
106 this(newVector(columnCount), rowCount);
107 }
108
109 /**
110 * Constructs a <code>DefaultTableModel</code> with as many columns
111 * as there are elements in <code>columnNames</code>
112 * and <code>rowCount</code> of <code>null</code>
113 * object values. Each column's name will be taken from
114 * the <code>columnNames</code> vector.
115 *
116 * @param columnNames <code>vector</code> containing the names
117 * of the new columns; if this is
118 * <code>null</code> then the model has no columns
119 * @param rowCount the number of rows the table holds
120 * @see #setDataVector
121 * @see #setValueAt
122 */
123 public DefaultTableModel(Vector columnNames, int rowCount) {
124 setDataVector(newVector(rowCount), columnNames);
125 }
126
127 /**
128 * Constructs a <code>DefaultTableModel</code> with as many
129 * columns as there are elements in <code>columnNames</code>
130 * and <code>rowCount</code> of <code>null</code>
131 * object values. Each column's name will be taken from
132 * the <code>columnNames</code> array.
133 *
134 * @param columnNames <code>array</code> containing the names
135 * of the new columns; if this is
136 * <code>null</code> then the model has no columns
137 * @param rowCount the number of rows the table holds
138 * @see #setDataVector
139 * @see #setValueAt
140 */
141 public DefaultTableModel(Object[] columnNames, int rowCount) {
142 this(convertToVector(columnNames), rowCount);
143 }
144
145 /**
146 * Constructs a <code>DefaultTableModel</code> and initializes the table
147 * by passing <code>data</code> and <code>columnNames</code>
148 * to the <code>setDataVector</code> method.
149 *
150 * @param data the data of the table, a <code>Vector</code>
151 * of <code>Vector</code>s of <code>Object</code>
152 * values
153 * @param columnNames <code>vector</code> containing the names
154 * of the new columns
155 * @see #getDataVector
156 * @see #setDataVector
157 */
158 public DefaultTableModel(Vector data, Vector columnNames) {
159 setDataVector(data, columnNames);
160 }
161
162 /**
163 * Constructs a <code>DefaultTableModel</code> and initializes the table
164 * by passing <code>data</code> and <code>columnNames</code>
165 * to the <code>setDataVector</code>
166 * method. The first index in the <code>Object[][]</code> array is
167 * the row index and the second is the column index.
168 *
169 * @param data the data of the table
170 * @param columnNames the names of the columns
171 * @see #getDataVector
172 * @see #setDataVector
173 */
174 public DefaultTableModel(Object[][] data, Object[] columnNames) {
175 setDataVector(data, columnNames);
176 }
177
178 /**
179 * Returns the <code>Vector</code> of <code>Vectors</code>
180 * that contains the table's
181 * data values. The vectors contained in the outer vector are
182 * each a single row of values. In other words, to get to the cell
183 * at row 1, column 5: <p>
184 *
185 * <code>((Vector)getDataVector().elementAt(1)).elementAt(5);</code><p>
186 *
187 * @return the vector of vectors containing the tables data values
188 *
189 * @see #newDataAvailable
190 * @see #newRowsAdded
191 * @see #setDataVector
192 */
193 public Vector getDataVector() {
194 return dataVector;
195 }
196
197 private static Vector nonNullVector(Vector v) {
198 return (v != null) ? v : new Vector();
199 }
200
201 /**
202 * Replaces the current <code>dataVector</code> instance variable
203 * with the new <code>Vector</code> of rows, <code>dataVector</code>.
204 * Each row is represented in <code>dataVector</code> as a
205 * <code>Vector</code> of <code>Object</code> values.
206 * <code>columnIdentifiers</code> are the names of the new
207 * columns. The first name in <code>columnIdentifiers</code> is
208 * mapped to column 0 in <code>dataVector</code>. Each row in
209 * <code>dataVector</code> is adjusted to match the number of
210 * columns in <code>columnIdentifiers</code>
211 * either by truncating the <code>Vector</code> if it is too long,
212 * or adding <code>null</code> values if it is too short.
213 * <p>Note that passing in a <code>null</code> value for
214 * <code>dataVector</code> results in unspecified behavior,
215 * an possibly an exception.
216 *
217 * @param dataVector the new data vector
218 * @param columnIdentifiers the names of the columns
219 * @see #getDataVector
220 */
221 public void setDataVector(Vector dataVector, Vector columnIdentifiers) {
222 this.dataVector = nonNullVector(dataVector);
223 this.columnIdentifiers = nonNullVector(columnIdentifiers);
224 justifyRows(0, getRowCount());
225 fireTableStructureChanged();
226 }
227
228 /**
229 * Replaces the value in the <code>dataVector</code> instance
230 * variable with the values in the array <code>dataVector</code>.
231 * The first index in the <code>Object[][]</code>
232 * array is the row index and the second is the column index.
233 * <code>columnIdentifiers</code> are the names of the new columns.
234 *
235 * @param dataVector the new data vector
236 * @param columnIdentifiers the names of the columns
237 * @see #setDataVector(Vector, Vector)
238 */
239 public void setDataVector(Object[][] dataVector, Object[] columnIdentifiers) {
240 setDataVector(convertToVector(dataVector), convertToVector(columnIdentifiers));
241 }
242
243 /**
244 * Equivalent to <code>fireTableChanged</code>.
245 *
246 * @param event the change event
247 *
248 */
249 public void newDataAvailable(TableModelEvent event) {
250 fireTableChanged(event);
251 }
252
253 //
254 // Manipulating rows
255 //
256
257 private void justifyRows(int from, int to) {
258 // Sometimes the DefaultTableModel is subclassed
259 // instead of the AbstractTableModel by mistake.
260 // Set the number of rows for the case when getRowCount
261 // is overridden.
262 dataVector.setSize(getRowCount());
263
264 for (int i = from; i < to; i++) {
265 if (dataVector.elementAt(i) == null) {
266 dataVector.setElementAt(new Vector(), i);
267 }
268 ((Vector)dataVector.elementAt(i)).setSize(getColumnCount());
269 }
270 }
271
272 /**
273 * Ensures that the new rows have the correct number of columns.
274 * This is accomplished by using the <code>setSize</code> method in
275 * <code>Vector</code> which truncates vectors
276 * which are too long, and appends <code>null</code>s if they
277 * are too short.
278 * This method also sends out a <code>tableChanged</code>
279 * notification message to all the listeners.
280 *
281 * @param e this <code>TableModelEvent</code> describes
282 * where the rows were added.
283 * If <code>null</code> it assumes
284 * all the rows were newly added
285 * @see #getDataVector
286 */
287 public void newRowsAdded(TableModelEvent e) {
288 justifyRows(e.getFirstRow(), e.getLastRow() + 1);
289 fireTableChanged(e);
290 }
291
292 /**
293 * Equivalent to <code>fireTableChanged</code>.
294 *
295 * @param event the change event
296 *
297 */
298 public void rowsRemoved(TableModelEvent event) {
299 fireTableChanged(event);
300 }
301
302 /**
303 * Obsolete as of Java 2 platform v1.3. Please use <code>setRowCount</code> instead.
304 */
305 /*
306 * Sets the number of rows in the model. If the new size is greater
307 * than the current size, new rows are added to the end of the model
308 * If the new size is less than the current size, all
309 * rows at index <code>rowCount</code> and greater are discarded. <p>
310 *
311 * @param rowCount the new number of rows
312 * @see #setRowCount
313 */
314 public void setNumRows(int rowCount) {
315 int old = getRowCount();
316 if (old == rowCount) {
317 return;
318 }
319 dataVector.setSize(rowCount);
320 if (rowCount <= old) {
321 fireTableRowsDeleted(rowCount, old-1);
322 }
323 else {
324 justifyRows(old, rowCount);
325 fireTableRowsInserted(old, rowCount-1);
326 }
327 }
328
329 /**
330 * Sets the number of rows in the model. If the new size is greater
331 * than the current size, new rows are added to the end of the model
332 * If the new size is less than the current size, all
333 * rows at index <code>rowCount</code> and greater are discarded. <p>
334 *
335 * @see #setColumnCount
336 * @since 1.3
337 */
338 public void setRowCount(int rowCount) {
339 setNumRows(rowCount);
340 }
341
342 /**
343 * Adds a row to the end of the model. The new row will contain
344 * <code>null</code> values unless <code>rowData</code> is specified.
345 * Notification of the row being added will be generated.
346 *
347 * @param rowData optional data of the row being added
348 */
349 public void addRow(Vector rowData) {
350 insertRow(getRowCount(), rowData);
351 }
352
353 /**
354 * Adds a row to the end of the model. The new row will contain
355 * <code>null</code> values unless <code>rowData</code> is specified.
356 * Notification of the row being added will be generated.
357 *
358 * @param rowData optional data of the row being added
359 */
360 public void addRow(Object[] rowData) {
361 addRow(convertToVector(rowData));
362 }
363
364 /**
365 * Inserts a row at <code>row</code> in the model. The new row
366 * will contain <code>null</code> values unless <code>rowData</code>
367 * is specified. Notification of the row being added will be generated.
368 *
369 * @param row the row index of the row to be inserted
370 * @param rowData optional data of the row being added
371 * @exception ArrayIndexOutOfBoundsException if the row was invalid
372 */
373 public void insertRow(int row, Vector rowData) {
374 dataVector.insertElementAt(rowData, row);
375 justifyRows(row, row+1);
376 fireTableRowsInserted(row, row);
377 }
378
379 /**
380 * Inserts a row at <code>row</code> in the model. The new row
381 * will contain <code>null</code> values unless <code>rowData</code>
382 * is specified. Notification of the row being added will be generated.
383 *
384 * @param row the row index of the row to be inserted
385 * @param rowData optional data of the row being added
386 * @exception ArrayIndexOutOfBoundsException if the row was invalid
387 */
388 public void insertRow(int row, Object[] rowData) {
389 insertRow(row, convertToVector(rowData));
390 }
391
392 private static int gcd(int i, int j) {
393 return (j == 0) ? i : gcd(j, i%j);
394 }
395
396 private static void rotate(Vector v, int a, int b, int shift) {
397 int size = b - a;
398 int r = size - shift;
399 int g = gcd(size, r);
400 for(int i = 0; i < g; i++) {
401 int to = i;
402 Object tmp = v.elementAt(a + to);
403 for(int from = (to + r) % size; from != i; from = (to + r) % size) {
404 v.setElementAt(v.elementAt(a + from), a + to);
405 to = from;
406 }
407 v.setElementAt(tmp, a + to);
408 }
409 }
410
411 /**
412 * Moves one or more rows from the inclusive range <code>start</code> to
413 * <code>end</code> to the <code>to</code> position in the model.
414 * After the move, the row that was at index <code>start</code>
415 * will be at index <code>to</code>.
416 * This method will send a <code>tableChanged</code> notification
417 * message to all the listeners. <p>
418 *
419 * <pre>
420 * Examples of moves:
421 * <p>
422 * 1. moveRow(1,3,5);
423 * a|B|C|D|e|f|g|h|i|j|k - before
424 * a|e|f|g|h|B|C|D|i|j|k - after
425 * <p>
426 * 2. moveRow(6,7,1);
427 * a|b|c|d|e|f|G|H|i|j|k - before
428 * a|G|H|b|c|d|e|f|i|j|k - after
429 * <p>
430 * </pre>
431 *
432 * @param start the starting row index to be moved
433 * @param end the ending row index to be moved
434 * @param to the destination of the rows to be moved
435 * @exception ArrayIndexOutOfBoundsException if any of the elements
436 * would be moved out of the table's range
437 *
438 */
439 public void moveRow(int start, int end, int to) {
440 int shift = to - start;
441 int first, last;
442 if (shift < 0) {
443 first = to;
444 last = end;
445 }
446 else {
447 first = start;
448 last = to + end - start;
449 }
450 rotate(dataVector, first, last + 1, shift);
451
452 fireTableRowsUpdated(first, last);
453 }
454
455 /**
456 * Removes the row at <code>row</code> from the model. Notification
457 * of the row being removed will be sent to all the listeners.
458 *
459 * @param row the row index of the row to be removed
460 * @exception ArrayIndexOutOfBoundsException if the row was invalid
461 */
462 public void removeRow(int row) {
463 dataVector.removeElementAt(row);
464 fireTableRowsDeleted(row, row);
465 }
466
467 //
468 // Manipulating columns
469 //
470
471 /**
472 * Replaces the column identifiers in the model. If the number of
473 * <code>newIdentifier</code>s is greater than the current number
474 * of columns, new columns are added to the end of each row in the model.
475 * If the number of <code>newIdentifier</code>s is less than the current
476 * number of columns, all the extra columns at the end of a row are
477 * discarded. <p>
478 *
479 * @param columnIdentifiers vector of column identifiers. If
480 * <code>null</code>, set the model
481 * to zero columns
482 * @see #setNumRows
483 */
484 public void setColumnIdentifiers(Vector columnIdentifiers) {
485 setDataVector(dataVector, columnIdentifiers);
486 }
487
488 /**
489 * Replaces the column identifiers in the model. If the number of
490 * <code>newIdentifier</code>s is greater than the current number
491 * of columns, new columns are added to the end of each row in the model.
492 * If the number of <code>newIdentifier</code>s is less than the current
493 * number of columns, all the extra columns at the end of a row are
494 * discarded. <p>
495 *
496 * @param newIdentifiers array of column identifiers.
497 * If <code>null</code>, set
498 * the model to zero columns
499 * @see #setNumRows
500 */
501 public void setColumnIdentifiers(Object[] newIdentifiers) {
502 setColumnIdentifiers(convertToVector(newIdentifiers));
503 }
504
505 /**
506 * Sets the number of columns in the model. If the new size is greater
507 * than the current size, new columns are added to the end of the model
508 * with <code>null</code> cell values.
509 * If the new size is less than the current size, all columns at index
510 * <code>columnCount</code> and greater are discarded.
511 *
512 * @param columnCount the new number of columns in the model
513 *
514 * @see #setColumnCount
515 * @since 1.3
516 */
517 public void setColumnCount(int columnCount) {
518 columnIdentifiers.setSize(columnCount);
519 justifyRows(0, getRowCount());
520 fireTableStructureChanged();
521 }
522
523 /**
524 * Adds a column to the model. The new column will have the
525 * identifier <code>columnName</code>, which may be null. This method
526 * will send a
527 * <code>tableChanged</code> notification message to all the listeners.
528 * This method is a cover for <code>addColumn(Object, Vector)</code> which
529 * uses <code>null</code> as the data vector.
530 *
531 * @param columnName the identifier of the column being added
532 */
533 public void addColumn(Object columnName) {
534 addColumn(columnName, (Vector)null);
535 }
536
537 /**
538 * Adds a column to the model. The new column will have the
539 * identifier <code>columnName</code>, which may be null.
540 * <code>columnData</code> is the
541 * optional vector of data for the column. If it is <code>null</code>
542 * the column is filled with <code>null</code> values. Otherwise,
543 * the new data will be added to model starting with the first
544 * element going to row 0, etc. This method will send a
545 * <code>tableChanged</code> notification message to all the listeners.
546 *
547 * @param columnName the identifier of the column being added
548 * @param columnData optional data of the column being added
549 */
550 public void addColumn(Object columnName, Vector columnData) {
551 columnIdentifiers.addElement(columnName);
552 if (columnData != null) {
553 int columnSize = columnData.size();
554 if (columnSize > getRowCount()) {
555 dataVector.setSize(columnSize);
556 }
557 justifyRows(0, getRowCount());
558 int newColumn = getColumnCount() - 1;
559 for(int i = 0; i < columnSize; i++) {
560 Vector row = (Vector)dataVector.elementAt(i);
561 row.setElementAt(columnData.elementAt(i), newColumn);
562 }
563 }
564 else {
565 justifyRows(0, getRowCount());
566 }
567
568 fireTableStructureChanged();
569 }
570
571 /**
572 * Adds a column to the model. The new column will have the
573 * identifier <code>columnName</code>. <code>columnData</code> is the
574 * optional array of data for the column. If it is <code>null</code>
575 * the column is filled with <code>null</code> values. Otherwise,
576 * the new data will be added to model starting with the first
577 * element going to row 0, etc. This method will send a
578 * <code>tableChanged</code> notification message to all the listeners.
579 *
580 * @see #addColumn(Object, Vector)
581 */
582 public void addColumn(Object columnName, Object[] columnData) {
583 addColumn(columnName, convertToVector(columnData));
584 }
585
586 //
587 // Implementing the TableModel interface
588 //
589
590 /**
591 * Returns the number of rows in this data table.
592 * @return the number of rows in the model
593 */
594 public int getRowCount() {
595 return dataVector.size();
596 }
597
598 /**
599 * Returns the number of columns in this data table.
600 * @return the number of columns in the model
601 */
602 public int getColumnCount() {
603 return columnIdentifiers.size();
604 }
605
606 /**
607 * Returns the column name.
608 *
609 * @return a name for this column using the string value of the
610 * appropriate member in <code>columnIdentifiers</code>.
611 * If <code>columnIdentifiers</code> does not have an entry
612 * for this index, returns the default
613 * name provided by the superclass.
614 */
615 public String getColumnName(int column) {
616 Object id = null;
617 // This test is to cover the case when
618 // getColumnCount has been subclassed by mistake ...
619 if (column < columnIdentifiers.size() && (column >= 0)) {
620 id = columnIdentifiers.elementAt(column);
621 }
622 return (id == null) ? super.getColumnName(column)
623 : id.toString();
624 }
625
626 /**
627 * Returns true regardless of parameter values.
628 *
629 * @param row the row whose value is to be queried
630 * @param column the column whose value is to be queried
631 * @return true
632 * @see #setValueAt
633 */
634 public boolean isCellEditable(int row, int column) {
635 return true;
636 }
637
638 /**
639 * Returns an attribute value for the cell at <code>row</code>
640 * and <code>column</code>.
641 *
642 * @param row the row whose value is to be queried
643 * @param column the column whose value is to be queried
644 * @return the value Object at the specified cell
645 * @exception ArrayIndexOutOfBoundsException if an invalid row or
646 * column was given
647 */
648 public Object getValueAt(int row, int column) {
649 Vector rowVector = (Vector)dataVector.elementAt(row);
650 return rowVector.elementAt(column);
651 }
652
653 /**
654 * Sets the object value for the cell at <code>column</code> and
655 * <code>row</code>. <code>aValue</code> is the new value. This method
656 * will generate a <code>tableChanged</code> notification.
657 *
658 * @param aValue the new value; this can be null
659 * @param row the row whose value is to be changed
660 * @param column the column whose value is to be changed
661 * @exception ArrayIndexOutOfBoundsException if an invalid row or
662 * column was given
663 */
664 public void setValueAt(Object aValue, int row, int column) {
665 Vector rowVector = (Vector)dataVector.elementAt(row);
666 rowVector.setElementAt(aValue, column);
667 fireTableCellUpdated(row, column);
668 }
669
670 //
671 // Protected Methods
672 //
673
674 /**
675 * Returns a vector that contains the same objects as the array.
676 * @param anArray the array to be converted
677 * @return the new vector; if <code>anArray</code> is <code>null</code>,
678 * returns <code>null</code>
679 */
680 protected static Vector convertToVector(Object[] anArray) {
681 if (anArray == null) {
682 return null;
683 }
684 Vector<Object> v = new Vector<Object>(anArray.length);
685 for (Object o : anArray) {
686 v.addElement(o);
687 }
688 return v;
689 }
690
691 /**
692 * Returns a vector of vectors that contains the same objects as the array.
693 * @param anArray the double array to be converted
694 * @return the new vector of vectors; if <code>anArray</code> is
695 * <code>null</code>, returns <code>null</code>
696 */
697 protected static Vector convertToVector(Object[][] anArray) {
698 if (anArray == null) {
699 return null;
700 }
701 Vector<Vector> v = new Vector<Vector>(anArray.length);
702 for (Object[] o : anArray) {
703 v.addElement(convertToVector(o));
704 }
705 return v;
706 }
707
708 } // End of class DefaultTableModel